"""
"""

import sys
import pytz
from datetime import datetime
from typing import Dict
from copy import copy

from vnsumscope.trader.constant import (
    Direction,
    Offset,
    Exchange,
    OrderType,
    Product,
    Status
)
from vnsumscope.trader.gateway import BaseGateway
from vnsumscope.trader.object import (
    PositionData,
    ContractData,
    CancelRequest,
    SubscribeRequest,
)
from vnsumscope.trader.utility import get_folder_path
from vnsumscope.trader.event import EVENT_PARAMETER


from vnsumscope.api import HftApi
from vnsumscope.trader.bond_object import (
    BondTickData,
    BondTradeData,
    BondOrderData,
    BondOrderRequest
)


STATUS_SUMSCOPE2VT = {
    "6": Status.SUBMITTING,
    "7": Status.REJECTED,
    "3": Status.NOTTRADED,
    "1": Status.PARTTRADED,
    "0": Status.ALLTRADED,
    "5": Status.CANCELLED,
    "a": Status.SUBMITTING,
}

DIRECTION_VT2SUMSCOPE = {
    Direction.LONG: "0",
    Direction.SHORT: "1"
}
DIRECTION_SUMSCOPE2VT = {v: k for k, v in DIRECTION_VT2SUMSCOPE.items()}

ORDERTYPE_VT2SUMSCOPE = {
    OrderType.LIMIT: "2",
    OrderType.MARKET: "1"
}
ORDERTYPE_SUMSCOPE2VT = {v: k for k, v in ORDERTYPE_VT2SUMSCOPE.items()}


MAX_FLOAT = sys.float_info.max
CHINA_TZ = pytz.timezone("Asia/Shanghai")


symbol_name_map = {}


class SumscopeGateway(BaseGateway):
    """
    VN Trader Gateway for SUMSCOPE .
    """

    default_setting = {
        "用户名": "10002",
        "密码": "",
        "服务器": "47.100.198.101:32201",
    }

    exchanges = [Exchange.CFETS]

    def __init__(self, event_engine):
        """Constructor"""
        super().__init__(event_engine, "SUMSCOPE")

        self.api = SumscopeApi(self)

    def connect(self, setting: dict):
        """"""
        customerid = setting["用户名"]
        password = setting["密码"]
        address = setting["服务器"]
        
        if not address.startswith("tcp://"):
            address = "tcp://" + address

        self.api.connect(address, customerid, password)

    def subscribe(self, req: SubscribeRequest):
        """"""
        self.api.subscribe(req)

    def send_order(self, req: BondOrderRequest):
        """"""
        vt_orderid = self.api.send_order(req)
        return vt_orderid

    def cancel_order(self, req: CancelRequest):
        """"""
        self.api.cancel_order(req)

    def query_account(self):
        """"""
        pass

    def query_position(self):
        """"""
        pass

    def close(self):
        """"""
        self.api.close()

    def write_error(self, msg: str, error: dict):
        """"""
        error_id = error["ErrorID"]
        error_msg = error["ErrorMsg"]
        msg = f"{msg}，代码：{error_id}，信息：{error_msg}"
        self.write_log(msg)
    
    def on_paramerter(self, parameters: dict) -> None:
        """参数更新事件-vnsumscope"""
        self.on_event(EVENT_PARAMETER, parameters)


class SumscopeApi(HftApi):
    """"""

    def __init__(self, gateway: SumscopeGateway):
        """Constructor"""
        super().__init__()

        self.gateway: SumscopeGateway = gateway
        self.gateway_name: str = gateway.gateway_name

        self.reqid: int = 0
        self.order_ref: int = 0

        self.connect_status: bool = False
        self.login_status: bool = False
        self.login_failed: bool = False
        
        self.customerid: str = ""
        self.password: str = ""
        
        self.subscribed: Dict[str, SubscribeRequest] = {}
        self.ticks: Dict[str, BondTickData] = {}

    # def onRtnDepthMarketData(self, data: dict: dict):
    #     """
    #     Callback of tick data update.
    #     """
    #     # Filter data update with no timestamp
    #     if not data["UpdateTime"]:
    #         return

    #     symbol = data["InstrumentID"]
    #     exchange = symbol_exchange_map.get(symbol, "")
    #     if not exchange:
    #         return

    #     timestamp = f"{self.current_date} {data["UpdateTime"]}.{int(data["UpdateMillisec"]/100)}"
    #     dt = datetime.strptime(timestamp, "%Y%m%d %H:%M:%S.%f")
    #     dt = CHINA_TZ.localize(dt)

    #     tick = TickData(
    #         symbol=symbol,
    #         exchange=exchange,
    #         datetime=dt,
    #         name=symbol_name_map[symbol],
    #         volume=data["Volume"],
    #         open_interest=data["OpenInterest"],
    #         last_price=data["LastPrice"],
    #         limit_up=data["UpperLimitPrice"],
    #         limit_down=data["LowerLimitPrice"],
    #         open_price=adjust_price(data["OpenPrice"]),
    #         high_price=adjust_price(data["HighestPrice"]),
    #         low_price=adjust_price(data["LowestPrice"]),
    #         pre_close=adjust_price(data["PreClosePrice"]),
    #         bid_price_1=adjust_price(data["BidPrice1"]),
    #         ask_price_1=adjust_price(data["AskPrice1"]),
    #         bid_volume_1=data["BidVolume1"],
    #         ask_volume_1=data["AskVolume1"],
    #         gateway_name=self.gateway_name
    #     )

    #     if data["BidVolume2"] or data["AskVolume2"]:
    #         tick.bid_price_2 = adjust_price(data["BidPrice2"])
    #         tick.bid_price_3 = adjust_price(data["BidPrice3"])
    #         tick.bid_price_4 = adjust_price(data["BidPrice4"])
    #         tick.bid_price_5 = adjust_price(data["BidPrice5"])

    #         tick.ask_price_2 = adjust_price(data["AskPrice2"])
    #         tick.ask_price_3 = adjust_price(data["AskPrice3"])
    #         tick.ask_price_4 = adjust_price(data["AskPrice4"])
    #         tick.ask_price_5 = adjust_price(data["AskPrice5"])

    #         tick.bid_volume_2 = data["BidVolume2"]
    #         tick.bid_volume_3 = data["BidVolume3"]
    #         tick.bid_volume_4 = data["BidVolume4"]
    #         tick.bid_volume_5 = data["BidVolume5"]

    #         tick.ask_volume_2 = data["AskVolume2"]
    #         tick.ask_volume_3 = data["AskVolume3"]
    #         tick.ask_volume_4 = data["AskVolume4"]
    #         tick.ask_volume_5 = data["AskVolume5"]

    #     self.gateway.on_tick(tick)

    def connect(self, address: str, customerid: str, password: str) -> None:
        """
        Start connection to server.
        """
        self.customerid = customerid
        self.password = password

        # If not connected, then start connection first.
        if not self.connect_status:
            path = get_folder_path(self.gateway_name.lower())
            # self.createUserApi(str(path).encode("GBK"))
            self.createUserApi()

            self.registerFront(address)
            self.init()

            self.connect_status = True
        # If already connected, then login immediately.
        elif not self.login_status:
            self.login()

    def login(self) -> None:
        """
        Login onto server.
        """
        req = {
            "CustomerID": self.customerid,
            "Password": self.password,
            "MacAddress": "",
            "SubInstrumentMethod": "0",
            "IsRule": 1
        }

        self.reqid += 1
        self.reqUserLogin(req, self.reqid)

    def subscribe(self, req: SubscribeRequest) -> None:
        """
        Subscribe to tick data update.
        """
        if self.login_status:
            api_req = {
                "InstrumentID": convert_vt_symbol(req.symbol),
                "ExchangeID": "sim",
            }

            self.reqid += 1
            self.reqSubSTCMarketdata(api_req, self.reqid)

            if req.symbol not in self.ticks:
                tick = BondTickData(
                    symbol=req.symbol,
                    exchange=req.exchange,
                    datetime=datetime.now(),
                    name=symbol_name_map.get(req.symbol, ""),
                    gateway_name=self.gateway_name
                )
                self.ticks[req.symbol] = tick

        self.subscribed[req.symbol] = req

    def send_order(self, req: BondOrderRequest) -> str:
        """
        Send new order.
        """
        if req.type not in ORDERTYPE_VT2SUMSCOPE:
            self.gateway.write_log(f"当前接口不支持该类型的委托{req.type.value}")
            return ""

        self.order_ref += 1
        account_prefix = str(self.customerid).rjust(6, "0")
        orderid_suffix = str(self.order_ref).rjust(6, "0")
        orderid = f"{account_prefix}{orderid_suffix}"

        api_req = {
            "AccountID": str(self.customerid),
            "RuleID": int(self.customerid),
            "CommodityType": "B",
            "InstrumentID": convert_vt_symbol(req.symbol),
            "OrderPriceType": ORDERTYPE_VT2SUMSCOPE[req.type],
            "Direction": DIRECTION_VT2SUMSCOPE[req.direction],
            "OffsetFlag": DIRECTION_VT2SUMSCOPE[req.direction],
            "HedgeFlag": "1",
            "Price": req.price,
            "Volume": int(req.volume),
            "YTM": req.ytm,
            "CustomerID": str(self.customerid),
            "LegSettlType": 2,
            "OrderInnerID": orderid,
            "Reserver1": "",
            "Reserver2": "",
            "Reserver3": "",
            "Reserver4": ""
        }

        self.reqid += 1
        self.reqOrderInsert(api_req, self.reqid)

        order = req.create_order_data(orderid, self.gateway_name)
        order.ytm = req.ytm
        self.gateway.on_order(order)

        return order.vt_orderid

    def cancel_order(self, req: CancelRequest) -> None:
        """
        Cancel existing order.
        """
        api_req = {
            "OrderInnerID": req.orderid,
            "ActionFlag": "0",
        }

        self.reqid += 1
        self.reqOrderAction(api_req, self.reqid)

    def close(self) -> None:
        """"""
        if self.connect_status:
            self.exit()

    def load_contract(self) -> None:
        """"""
        contract = ContractData(
            symbol="200210",
            exchange=Exchange.CFETS,
            name="20国开10",
            product=Product.BOND,
            size=100,
            pricetick=0.0001,
            min_volume=1000,
            net_position=True,
            gateway_name=self.gateway_name
        )

        symbol_name_map[contract.symbol] = contract.name
        self.gateway.on_contract(contract)

        self.gateway.write_log("合约信息加载成功")

    def onFrontConnected(self) -> None:
        """服务器连接"""
        self.gateway.write_log("服务器连接成功")
        self.login()
        
    def onFrontDisconnected(self, n: int) -> None:
        """服务器断开"""
        self.login_status = False
        self.gateway.write_log(f"服务器连接断开，原因{n}")
        
    def onHeartBeatWarning(self, n: int) -> None:
        """心跳报警"""
        print(n)
        
    def onRspError(self, error: dict, n: int, last: bool) -> None:
        """错误"""
        self.gateway.write_error("请求失败", error)
    
    def onRspUserLogin(self, data: dict, error: dict, data2: dict, n: int, last: bool) -> None:
        """登陆回报"""
        if not error["ErrorID"]:
            self.login_status = True
            self.gateway.write_log("服务器登录成功")

            self.load_contract()
        else:
            self.login_failed = True
            self.gateway.write_error("服务器登录失败", error)
    
    def onRspUserLogout(self, data: dict, error: dict, n: int, last: bool) -> None:
        """登出回报"""
        pass
        
    def onRtnSTCMarketdata(self, data: dict) -> None:
        """行情推送"""
        symbol = data["BondID"]
        tick = self.ticks.get(symbol, None)
        if not tick:
            return

    def onRtnDepthMarketData(self, data: dict) -> None:
        """行情推送"""
        print("on rtn depth")
        print(data)

    def onRtnSTCTick(self, data: dict) -> None:
        """行情推送"""
        symbol = convert_sumscope_symbol(data["BondID"])
        tick = self.ticks.get(symbol, None)
        if not tick:
            return

        dt = datetime.strptime(data["MDTime"], "%Y-%m-%d %H:%M:%S")
        dt = CHINA_TZ.localize(dt)

        tick.bid_price_1 = round(data["BidPrice"], 4)
        tick.bid_volume_1 = data["BidVolume"]
        tick.bid_ytm_1 = round(data["BidYTM"], 4)
        tick.ask_price_1 = round(data["OfrPrice"], 4)
        tick.ask_volume_1 = data["OfrVolume"]
        tick.ask_ytm_1 = round(data["OfrYTM"], 4)
        tick.datetime = dt
        
        self.gateway.on_tick(copy(tick))
    
    def onRspOrderInsert(self, data: dict, error: dict, n: int, last: bool) -> None:
        """报单失败响应"""
        order = BondOrderData(
            symbol=convert_sumscope_symbol(data["InstrumentID"]),
            exchange=Exchange.CFETS,
            orderid=data["OrderInnerID"],
            direction=DIRECTION_SUMSCOPE2VT[data["Direction"]],
            offset=Offset.NONE,
            price=data["Price"],
            ytm=data["YTM"],
            volume=data["Volume"],
            status=Status.REJECTED,
            gateway_name=self.gateway_name
        )
        self.gateway.on_order(order)

        self.gateway.write_error("交易委托失败", error)
      
    def onRspOrderAction(self, data: dict, error: dict, n: int, last: bool) -> None:
        """撤单失败响应"""
        self.gateway.write_error("交易撤单失败", error)
    
    def onRspCalcBond(self, data: dict, error: dict, n: int, last: bool) -> None:
        """债券计算器"""
        pass
    
    def onRtnRuleIndicator(self, data: dict) -> None:
        """策略指标回报"""
        pass
    
    def onRtnRuleProp(self, data: dict) -> None:
        """策略参数回报"""
        self.gateway.on_paramerter(data)

    def onRspQryBond(self, data: dict, error: dict, n: int, last: bool) -> None:
        """债券查询响应"""
        pass
    
    def onRtnRuleOrder(self, data: dict) -> None:
        """报单回报"""
        orderid = data["OrderInnerID"]
        order_ref = int(orderid[6:])
        self.order_ref = max(order_ref, self.order_ref)

        timestamp = f"{data['InsertDate']} {data['InsertTime']}"
        dt = datetime.strptime(timestamp, "%Y%m%d %H:%M:%S")
        dt = CHINA_TZ.localize(dt)

        order = BondOrderData(
            symbol=convert_sumscope_symbol(data["InstrumentID"]),
            exchange=Exchange.CFETS,
            orderid=data["OrderInnerID"],
            type=ORDERTYPE_SUMSCOPE2VT[data["OrderPriceType"]],
            direction=DIRECTION_SUMSCOPE2VT[data["Direction"]],
            offset=Offset.NONE,
            price=data["Price"],
            ytm=data["YTM"],
            volume=data["Volume"],
            traded=data["VolumeTraded"],
            status=STATUS_SUMSCOPE2VT[data["OrderStatus"]],
            datetime=dt,
            gateway_name=self.gateway_name
        )
        self.gateway.on_order(order)
    
    def onRtnRuleTrade(self, data: dict) -> None:
        """成交回报"""
        timestamp = f"{data['TradeDate']} {data['TradeTime']}"
        dt = datetime.strptime(timestamp, "%Y%m%d %H:%M:%S")
        dt = CHINA_TZ.localize(dt)

        trade = BondTradeData(
            symbol=convert_sumscope_symbol(data["InstrumentID"]),
            exchange=Exchange.CFETS,
            orderid=data["OrderInnerID"],
            tradeid=data["TradeID"],
            direction=DIRECTION_SUMSCOPE2VT[data["Direction"]],
            offset=Offset.NONE,
            price=data["TradePrice"],
            ytm=data["YTM"],
            volume=data["VolumeTraded"],
            datetime=dt,
            gateway_name=self.gateway_name
        )
        self.gateway.on_trade(trade)
    
    def onRtnRulePosition(self, data: dict) -> None:
        """持仓回报"""    
        if data["Position"]:
            price=data["OpenCost"] / (data["Position"] * 100)
        else:
            price = 0

        position = PositionData(
            symbol=convert_sumscope_symbol(data["InstrumentID"]),
            exchange=Exchange.CFETS,
            direction=Direction.NET,
            volume=data["Position"],
            price=price,
            gateway_name=self.gateway_name
        )
        self.gateway.on_position(position)
    
    def onRtnRule(self, data: dict) -> None:
        """策略回报"""
        pass


def adjust_price(price: float) -> float:
    """"""
    if price == MAX_FLOAT:
        price = 0
    return price


def convert_vt_symbol(symbol):
    return symbol + ".IB"


def convert_sumscope_symbol(symbol):
    return symbol.replace(".IB", "")
